/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.protocol.giopmsgheaders;

import java.io.IOException;
import java.lang.Class;
import java.lang.reflect.Constructor;
import java.nio.ByteBuffer;
import java.util.Iterator;

import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.MARSHAL;
import org.omg.CORBA.Principal;
import org.omg.CORBA.SystemException;
import org.omg.IOP.TaggedProfile;

import com.sun.corba.se.pept.transport.ByteBufferPool;

import com.sun.corba.se.spi.ior.ObjectKey;
import com.sun.corba.se.spi.ior.ObjectId;
import com.sun.corba.se.spi.ior.IOR;
import com.sun.corba.se.spi.ior.ObjectKeyFactory;
import com.sun.corba.se.spi.ior.iiop.IIOPProfile;
import com.sun.corba.se.spi.ior.iiop.IIOPFactories;
import com.sun.corba.se.spi.ior.iiop.IIOPProfileTemplate;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.spi.ior.iiop.RequestPartitioningComponent;
import com.sun.corba.se.spi.logging.CORBALogDomains;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.transport.CorbaConnection;
import com.sun.corba.se.spi.transport.ReadTimeouts;

import com.sun.corba.se.spi.servicecontext.ServiceContexts;
import com.sun.corba.se.impl.encoding.ByteBufferWithInfo;
import com.sun.corba.se.impl.encoding.CDRInputStream_1_0;
import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.orbutil.ORBUtility;
import com.sun.corba.se.impl.orbutil.ORBConstants;
import com.sun.corba.se.impl.protocol.AddressingDispositionException;

import sun.corba.SharedSecrets;

/**
 * This class acts as the base class for the various GIOP message types. This
 * also serves as a factory to create various message types. We currently
 * support GIOP 1.0, 1.1 and 1.2 message types.
 *
 * @author Ram Jeyaraman 05/14/2000
 */

public abstract class MessageBase implements Message {

  // This is only used when the giopDebug flag is
  // turned on.
  public byte[] giopHeader;
  private ByteBuffer byteBuffer;
  private int threadPoolToUse;

  // (encodingVersion == 0x00) implies CDR encoding,
  // (encodingVersion >  0x00) implies Java serialization version.
  byte encodingVersion = (byte) Message.CDR_ENC_VERSION;

  private static ORBUtilSystemException wrapper =
      ORBUtilSystemException.get(CORBALogDomains.RPC_PROTOCOL);

  // Static methods

  public static String typeToString(int type) {
    return typeToString((byte) type);
  }

  public static String typeToString(byte type) {
    String result = type + "/";
    switch (type) {
      case GIOPRequest:
        result += "GIOPRequest";
        break;
      case GIOPReply:
        result += "GIOPReply";
        break;
      case GIOPCancelRequest:
        result += "GIOPCancelRequest";
        break;
      case GIOPLocateRequest:
        result += "GIOPLocateRequest";
        break;
      case GIOPLocateReply:
        result += "GIOPLocateReply";
        break;
      case GIOPCloseConnection:
        result += "GIOPCloseConnection";
        break;
      case GIOPMessageError:
        result += "GIOPMessageError";
        break;
      case GIOPFragment:
        result += "GIOPFragment";
        break;
      default:
        result += "Unknown";
        break;
    }
    return result;
  }

  public static MessageBase readGIOPMessage(ORB orb, CorbaConnection connection) {
    MessageBase msg = readGIOPHeader(orb, connection);
    msg = (MessageBase) readGIOPBody(orb, connection, (Message) msg);
    return msg;
  }

  public static MessageBase readGIOPHeader(ORB orb, CorbaConnection connection) {
    MessageBase msg = null;
    ReadTimeouts readTimeouts =
        orb.getORBData().getTransportTCPReadTimeouts();

    ByteBuffer buf = null;

    try {
      buf = connection.read(GIOPMessageHeaderLength,
          0, GIOPMessageHeaderLength,
          readTimeouts.get_max_giop_header_time_to_wait());
    } catch (IOException e) {
      throw wrapper.ioexceptionWhenReadingConnection(e);
    }

    if (orb.giopDebugFlag) {
      // Since this is executed in debug mode only the overhead of
      // using a View Buffer is not an issue. We'll also use a
      // read-only View Buffer so we don't disturb the state of
      // byteBuffer.
      dprint(".readGIOPHeader: " + typeToString(buf.get(7)));
      dprint(".readGIOPHeader: GIOP header is: ");
      ByteBuffer viewBuffer = buf.asReadOnlyBuffer();
      viewBuffer.position(0).limit(GIOPMessageHeaderLength);
      ByteBufferWithInfo bbwi = new ByteBufferWithInfo(orb, viewBuffer);
      bbwi.buflen = GIOPMessageHeaderLength;
      CDRInputStream_1_0.printBuffer(bbwi);
    }

    // Sanity checks

        /*
         * check for magic corruption
         * check for version incompatibility
         * check if fragmentation is allowed based on mesg type.
            . 1.0 fragmentation disallowed; FragmentMessage is non-existent.
            . 1.1 only {Request, Reply} msgs maybe fragmented.
            . 1.2 only {Request, Reply, LocateRequest, LocateReply} msgs
              maybe fragmented.
        */

    int b1, b2, b3, b4;

    b1 = (buf.get(0) << 24) & 0xFF000000;
    b2 = (buf.get(1) << 16) & 0x00FF0000;
    b3 = (buf.get(2) << 8) & 0x0000FF00;
    b4 = (buf.get(3) << 0) & 0x000000FF;
    int magic = (b1 | b2 | b3 | b4);

    if (magic != GIOPBigMagic) {
      // If Magic is incorrect, it is an error.
      // ACTION : send MessageError and close the connection.
      throw wrapper.giopMagicError(CompletionStatus.COMPLETED_MAYBE);
    }

    // Extract the encoding version from the request GIOP Version,
    // if it contains an encoding, and set GIOP version appropriately.
    // For Java serialization, we use GIOP Version 1.2 message format.
    byte requestEncodingVersion = Message.CDR_ENC_VERSION;
    if ((buf.get(4) == 0x0D) &&
        (buf.get(5) <= Message.JAVA_ENC_VERSION) &&
        (buf.get(5) > Message.CDR_ENC_VERSION) &&
        orb.getORBData().isJavaSerializationEnabled()) {
      // Entering this block means the request is using Java encoding,
      // and the encoding version is <= this ORB's Java encoding version.
      requestEncodingVersion = buf.get(5);
      buf.put(4, (byte) 0x01);
      buf.put(5, (byte) 0x02);
    }

    GIOPVersion orbVersion = orb.getORBData().getGIOPVersion();

    if (orb.giopDebugFlag) {
      dprint(".readGIOPHeader: Message GIOP version: "
          + buf.get(4) + '.' + buf.get(5));
      dprint(".readGIOPHeader: ORB Max GIOP Version: "
          + orbVersion);
    }

    if ((buf.get(4) > orbVersion.getMajor()) ||
        ((buf.get(4) == orbVersion.getMajor()) && (buf.get(5) > orbVersion.getMinor()))
        ) {
      // For requests, sending ORB should use the version info
      // published in the IOR or may choose to use a <= version
      // for requests. If the version is greater than published version,
      // it is an error.

      // For replies, the ORB should always receive a version it supports
      // or less, but never greater (except for MessageError)

      // ACTION : Send back a MessageError() with the the highest version
      // the server ORB supports, and close the connection.
      if (buf.get(7) != GIOPMessageError) {
        throw wrapper.giopVersionError(CompletionStatus.COMPLETED_MAYBE);
      }
    }

    AreFragmentsAllowed(buf.get(4), buf.get(5), buf.get(6), buf.get(7));

    // create appropriate messages types

    switch (buf.get(7)) {

      case GIOPRequest:
        if (orb.giopDebugFlag) {
          dprint(".readGIOPHeader: creating RequestMessage");
        }
        //msg = new RequestMessage(orb.giopDebugFlag);
        if ((buf.get(4) == 0x01) && (buf.get(5) == 0x00)) { // 1.0
          msg = new RequestMessage_1_0(orb);
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x01)) { // 1.1
          msg = new RequestMessage_1_1(orb);
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x02)) { // 1.2
          msg = new RequestMessage_1_2(orb);
        } else {
          throw wrapper.giopVersionError(
              CompletionStatus.COMPLETED_MAYBE);
        }
        break;

      case GIOPLocateRequest:
        if (orb.giopDebugFlag) {
          dprint(".readGIOPHeader: creating LocateRequestMessage");
        }
        //msg = new LocateRequestMessage(orb.giopDebugFlag);
        if ((buf.get(4) == 0x01) && (buf.get(5) == 0x00)) { // 1.0
          msg = new LocateRequestMessage_1_0(orb);
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x01)) { // 1.1
          msg = new LocateRequestMessage_1_1(orb);
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x02)) { // 1.2
          msg = new LocateRequestMessage_1_2(orb);
        } else {
          throw wrapper.giopVersionError(
              CompletionStatus.COMPLETED_MAYBE);
        }
        break;

      case GIOPCancelRequest:
        if (orb.giopDebugFlag) {
          dprint(".readGIOPHeader: creating CancelRequestMessage");
        }
        //msg = new CancelRequestMessage(orb.giopDebugFlag);
        if ((buf.get(4) == 0x01) && (buf.get(5) == 0x00)) { // 1.0
          msg = new CancelRequestMessage_1_0();
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x01)) { // 1.1
          msg = new CancelRequestMessage_1_1();
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x02)) { // 1.2
          msg = new CancelRequestMessage_1_2();
        } else {
          throw wrapper.giopVersionError(
              CompletionStatus.COMPLETED_MAYBE);
        }
        break;

      case GIOPReply:
        if (orb.giopDebugFlag) {
          dprint(".readGIOPHeader: creating ReplyMessage");
        }
        //msg = new ReplyMessage(orb.giopDebugFlag);
        if ((buf.get(4) == 0x01) && (buf.get(5) == 0x00)) { // 1.0
          msg = new ReplyMessage_1_0(orb);
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x01)) { // 1.1
          msg = new ReplyMessage_1_1(orb);
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x02)) { // 1.2
          msg = new ReplyMessage_1_2(orb);
        } else {
          throw wrapper.giopVersionError(
              CompletionStatus.COMPLETED_MAYBE);
        }
        break;

      case GIOPLocateReply:
        if (orb.giopDebugFlag) {
          dprint(".readGIOPHeader: creating LocateReplyMessage");
        }
        //msg = new LocateReplyMessage(orb.giopDebugFlag);
        if ((buf.get(4) == 0x01) && (buf.get(5) == 0x00)) { // 1.0
          msg = new LocateReplyMessage_1_0(orb);
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x01)) { // 1.1
          msg = new LocateReplyMessage_1_1(orb);
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x02)) { // 1.2
          msg = new LocateReplyMessage_1_2(orb);
        } else {
          throw wrapper.giopVersionError(
              CompletionStatus.COMPLETED_MAYBE);
        }
        break;

      case GIOPCloseConnection:
      case GIOPMessageError:
        if (orb.giopDebugFlag) {
          dprint(".readGIOPHeader: creating Message for CloseConnection or MessageError");
        }
        // REVISIT a MessageError  may contain the highest version server
        // can support. In such a case, a new request may be made with the
        // correct version or the connection be simply closed. Note the
        // connection may have been closed by the server.
        //msg = new Message(orb.giopDebugFlag);
        if ((buf.get(4) == 0x01) && (buf.get(5) == 0x00)) { // 1.0
          msg = new Message_1_0();
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x01)) { // 1.1
          msg = new Message_1_1();
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x02)) { // 1.2
          msg = new Message_1_1();
        } else {
          throw wrapper.giopVersionError(
              CompletionStatus.COMPLETED_MAYBE);
        }
        break;

      case GIOPFragment:
        if (orb.giopDebugFlag) {
          dprint(".readGIOPHeader: creating FragmentMessage");
        }
        //msg = new FragmentMessage(orb.giopDebugFlag);
        if ((buf.get(4) == 0x01) && (buf.get(5) == 0x00)) { // 1.0
          // not possible (error checking done already)
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x01)) { // 1.1
          msg = new FragmentMessage_1_1();
        } else if ((buf.get(4) == 0x01) && (buf.get(5) == 0x02)) { // 1.2
          msg = new FragmentMessage_1_2();
        } else {
          throw wrapper.giopVersionError(
              CompletionStatus.COMPLETED_MAYBE);
        }
        break;

      default:
        if (orb.giopDebugFlag) {
          dprint(".readGIOPHeader: UNKNOWN MESSAGE TYPE: "
              + buf.get(7));
        }
        // unknown message type ?
        // ACTION : send MessageError and close the connection
        throw wrapper.giopVersionError(
            CompletionStatus.COMPLETED_MAYBE);
    }

    //
    // Initialize the generic GIOP header instance variables.
    //

    if ((buf.get(4) == 0x01) && (buf.get(5) == 0x00)) { // 1.0
      Message_1_0 msg10 = (Message_1_0) msg;
      msg10.magic = magic;
      msg10.GIOP_version = new GIOPVersion(buf.get(4), buf.get(5));
      msg10.byte_order = (buf.get(6) == LITTLE_ENDIAN_BIT);
      // 'request partitioning' not supported on GIOP version 1.0
      // so just use the default thread pool, 0.
      msg.threadPoolToUse = 0;
      msg10.message_type = buf.get(7);
      msg10.message_size = readSize(buf.get(8), buf.get(9), buf.get(10), buf.get(11),
          msg10.isLittleEndian()) +
          GIOPMessageHeaderLength;
    } else { // 1.1 & 1.2
      Message_1_1 msg11 = (Message_1_1) msg;
      msg11.magic = magic;
      msg11.GIOP_version = new GIOPVersion(buf.get(4), buf.get(5));
      msg11.flags = (byte) (buf.get(6) & TRAILING_TWO_BIT_BYTE_MASK);
      // IMPORTANT: For 'request partitioning', the thread pool to use
      //            information is stored in the leading 6 bits of byte 6.
      //
      // IMPORTANT: Request partitioning is a PROPRIETARY EXTENSION !!!
      //
      // NOTE: Bitwise operators will promote a byte to an int before
      //       performing a bitwise operation and bytes, ints, longs, etc
      //       are signed types in Java. Thus, the need for the
      //       THREAD_POOL_TO_USE_MASK operation.
      msg.threadPoolToUse = (buf.get(6) >>> 2) & THREAD_POOL_TO_USE_MASK;
      msg11.message_type = buf.get(7);
      msg11.message_size =
          readSize(buf.get(8), buf.get(9), buf.get(10), buf.get(11),
              msg11.isLittleEndian()) + GIOPMessageHeaderLength;
    }

    if (orb.giopDebugFlag) {
      // Since this is executed in debug mode only the overhead of
      // using a View Buffer is not an issue. We'll also use a
      // read-only View Buffer so we don't disturb the state of
      // byteBuffer.
      dprint(".readGIOPHeader: header construction complete.");

      // For debugging purposes, save the 12 bytes of the header
      ByteBuffer viewBuf = buf.asReadOnlyBuffer();
      byte[] msgBuf = new byte[GIOPMessageHeaderLength];
      viewBuf.position(0).limit(GIOPMessageHeaderLength);
      viewBuf.get(msgBuf, 0, msgBuf.length);
      // REVISIT: is giopHeader still used?
      ((MessageBase) msg).giopHeader = msgBuf;
    }

    msg.setByteBuffer(buf);
    msg.setEncodingVersion(requestEncodingVersion);

    return msg;
  }

  public static Message readGIOPBody(ORB orb,
      CorbaConnection connection,
      Message msg) {
    ReadTimeouts readTimeouts =
        orb.getORBData().getTransportTCPReadTimeouts();
    ByteBuffer buf = msg.getByteBuffer();

    buf.position(MessageBase.GIOPMessageHeaderLength);
    int msgSizeMinusHeader =
        msg.getSize() - MessageBase.GIOPMessageHeaderLength;
    try {
      buf = connection.read(buf,
          GIOPMessageHeaderLength, msgSizeMinusHeader,
          readTimeouts.get_max_time_to_wait());
    } catch (IOException e) {
      throw wrapper.ioexceptionWhenReadingConnection(e);
    }

    msg.setByteBuffer(buf);

    if (orb.giopDebugFlag) {
      dprint(".readGIOPBody: received message:");
      ByteBuffer viewBuffer = buf.asReadOnlyBuffer();
      viewBuffer.position(0).limit(msg.getSize());
      ByteBufferWithInfo bbwi = new ByteBufferWithInfo(orb, viewBuffer);
      CDRInputStream_1_0.printBuffer(bbwi);
    }

    return msg;
  }

  private static RequestMessage createRequest(
      ORB orb, GIOPVersion gv, byte encodingVersion, int request_id,
      boolean response_expected, byte[] object_key, String operation,
      ServiceContexts service_contexts, Principal requesting_principal) {

    if (gv.equals(GIOPVersion.V1_0)) { // 1.0
      return new RequestMessage_1_0(orb, service_contexts, request_id,
          response_expected, object_key,
          operation, requesting_principal);
    } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
      return new RequestMessage_1_1(orb, service_contexts, request_id,
          response_expected, new byte[]{0x00, 0x00, 0x00},
          object_key, operation, requesting_principal);
    } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
      // Note: Currently we use response_expected flag to decide if the
      // call is oneway or not. Ideally, it is possible to expect a
      // response on a oneway call too, but we do not support it now.
      byte response_flags = 0x03;
      if (response_expected) {
        response_flags = 0x03;
      } else {
        response_flags = 0x00;
      }
            /*
            // REVISIT The following is the correct way to do it. This gives
            // more flexibility.
            if ((DII::INV_NO_RESPONSE == false) && response_expected) {
                response_flags = 0x03; // regular two-way
            } else if ((DII::INV_NO_RESPONSE == false) && !response_expected) {
                // this condition is not possible
            } else if ((DII::INV_NO_RESPONSE == true) && response_expected) {
                // oneway, but we need response for LocationForwards or
                // SystemExceptions.
                response_flags = 0x01;
            } else if ((DII::INV_NO_RESPONSE == true) && !response_expected) {
                // oneway, no response required
                response_flags = 0x00;
            }
            */
      TargetAddress target = new TargetAddress();
      target.object_key(object_key);
      RequestMessage msg =
          new RequestMessage_1_2(orb, request_id, response_flags,
              new byte[]{0x00, 0x00, 0x00},
              target, operation, service_contexts);
      msg.setEncodingVersion(encodingVersion);
      return msg;
    } else {
      throw wrapper.giopVersionError(
          CompletionStatus.COMPLETED_MAYBE);
    }
  }

  public static RequestMessage createRequest(
      ORB orb, GIOPVersion gv, byte encodingVersion, int request_id,
      boolean response_expected, IOR ior,
      short addrDisp, String operation,
      ServiceContexts service_contexts, Principal requesting_principal) {

    RequestMessage requestMessage = null;
    IIOPProfile profile = ior.getProfile();

    if (addrDisp == KeyAddr.value) {
      // object key will be used for target addressing
      profile = ior.getProfile();
      ObjectKey objKey = profile.getObjectKey();
      byte[] object_key = objKey.getBytes(orb);
      requestMessage =
          createRequest(orb, gv, encodingVersion, request_id,
              response_expected, object_key,
              operation, service_contexts,
              requesting_principal);
    } else {

      if (!(gv.equals(GIOPVersion.V1_2))) {
        // only object_key based target addressing is allowed for
        // GIOP 1.0 & 1.1
        throw wrapper.giopVersionError(
            CompletionStatus.COMPLETED_MAYBE);
      }

      // Note: Currently we use response_expected flag to decide if the
      // call is oneway or not. Ideally, it is possible to expect a
      // response on a oneway call too, but we do not support it now.
      byte response_flags = 0x03;
      if (response_expected) {
        response_flags = 0x03;
      } else {
        response_flags = 0x00;
      }

      TargetAddress target = new TargetAddress();
      if (addrDisp == ProfileAddr.value) { // iop profile will be used
        profile = ior.getProfile();
        target.profile(profile.getIOPProfile());
      } else if (addrDisp == ReferenceAddr.value) {  // ior will be used
        IORAddressingInfo iorInfo =
            new IORAddressingInfo(0, // profile index
                ior.getIOPIOR());
        target.ior(iorInfo);
      } else {
        // invalid target addressing disposition value
        throw wrapper.illegalTargetAddressDisposition(
            CompletionStatus.COMPLETED_NO);
      }

      requestMessage =
          new RequestMessage_1_2(orb, request_id, response_flags,
              new byte[]{0x00, 0x00, 0x00}, target,
              operation, service_contexts);
      requestMessage.setEncodingVersion(encodingVersion);
    }

    if (gv.supportsIORIIOPProfileComponents()) {
      // add request partitioning thread pool to use info
      int poolToUse = 0; // default pool
      IIOPProfileTemplate temp =
          (IIOPProfileTemplate) profile.getTaggedProfileTemplate();
      Iterator iter =
          temp.iteratorById(ORBConstants.TAG_REQUEST_PARTITIONING_ID);
      if (iter.hasNext()) {
        poolToUse =
            ((RequestPartitioningComponent) iter.next()).getRequestPartitioningId();
      }

      if (poolToUse < ORBConstants.REQUEST_PARTITIONING_MIN_THREAD_POOL_ID ||
          poolToUse > ORBConstants.REQUEST_PARTITIONING_MAX_THREAD_POOL_ID) {
        throw wrapper.invalidRequestPartitioningId(new Integer(poolToUse),
            new Integer(ORBConstants.REQUEST_PARTITIONING_MIN_THREAD_POOL_ID),
            new Integer(ORBConstants.REQUEST_PARTITIONING_MAX_THREAD_POOL_ID));
      }
      requestMessage.setThreadPoolToUse(poolToUse);
    }

    return requestMessage;
  }

  public static ReplyMessage createReply(
      ORB orb, GIOPVersion gv, byte encodingVersion, int request_id,
      int reply_status, ServiceContexts service_contexts, IOR ior) {

    if (gv.equals(GIOPVersion.V1_0)) { // 1.0
      return new ReplyMessage_1_0(orb, service_contexts, request_id,
          reply_status, ior);
    } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
      return new ReplyMessage_1_1(orb, service_contexts, request_id,
          reply_status, ior);
    } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
      ReplyMessage msg =
          new ReplyMessage_1_2(orb, request_id, reply_status,
              service_contexts, ior);
      msg.setEncodingVersion(encodingVersion);
      return msg;
    } else {
      throw wrapper.giopVersionError(
          CompletionStatus.COMPLETED_MAYBE);
    }
  }

  public static LocateRequestMessage createLocateRequest(
      ORB orb, GIOPVersion gv, byte encodingVersion,
      int request_id, byte[] object_key) {

    if (gv.equals(GIOPVersion.V1_0)) { // 1.0
      return new LocateRequestMessage_1_0(orb, request_id, object_key);
    } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
      return new LocateRequestMessage_1_1(orb, request_id, object_key);
    } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
      TargetAddress target = new TargetAddress();
      target.object_key(object_key);
      LocateRequestMessage msg =
          new LocateRequestMessage_1_2(orb, request_id, target);
      msg.setEncodingVersion(encodingVersion);
      return msg;
    } else {
      throw wrapper.giopVersionError(
          CompletionStatus.COMPLETED_MAYBE);
    }
  }

  public static LocateReplyMessage createLocateReply(
      ORB orb, GIOPVersion gv, byte encodingVersion,
      int request_id, int locate_status, IOR ior) {

    if (gv.equals(GIOPVersion.V1_0)) { // 1.0
      return new LocateReplyMessage_1_0(orb, request_id,
          locate_status, ior);
    } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
      return new LocateReplyMessage_1_1(orb, request_id,
          locate_status, ior);
    } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
      LocateReplyMessage msg =
          new LocateReplyMessage_1_2(orb, request_id,
              locate_status, ior);
      msg.setEncodingVersion(encodingVersion);
      return msg;
    } else {
      throw wrapper.giopVersionError(
          CompletionStatus.COMPLETED_MAYBE);
    }
  }

  public static CancelRequestMessage createCancelRequest(
      GIOPVersion gv, int request_id) {

    if (gv.equals(GIOPVersion.V1_0)) { // 1.0
      return new CancelRequestMessage_1_0(request_id);
    } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
      return new CancelRequestMessage_1_1(request_id);
    } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
      return new CancelRequestMessage_1_2(request_id);
    } else {
      throw wrapper.giopVersionError(
          CompletionStatus.COMPLETED_MAYBE);
    }
  }

  public static Message createCloseConnection(GIOPVersion gv) {
    if (gv.equals(GIOPVersion.V1_0)) { // 1.0
      return new Message_1_0(Message.GIOPBigMagic, false,
          Message.GIOPCloseConnection, 0);
    } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
      return new Message_1_1(Message.GIOPBigMagic, GIOPVersion.V1_1,
          FLAG_NO_FRAG_BIG_ENDIAN,
          Message.GIOPCloseConnection, 0);
    } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
      return new Message_1_1(Message.GIOPBigMagic, GIOPVersion.V1_2,
          FLAG_NO_FRAG_BIG_ENDIAN,
          Message.GIOPCloseConnection, 0);
    } else {
      throw wrapper.giopVersionError(
          CompletionStatus.COMPLETED_MAYBE);
    }
  }

  public static Message createMessageError(GIOPVersion gv) {
    if (gv.equals(GIOPVersion.V1_0)) { // 1.0
      return new Message_1_0(Message.GIOPBigMagic, false,
          Message.GIOPMessageError, 0);
    } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
      return new Message_1_1(Message.GIOPBigMagic, GIOPVersion.V1_1,
          FLAG_NO_FRAG_BIG_ENDIAN,
          Message.GIOPMessageError, 0);
    } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
      return new Message_1_1(Message.GIOPBigMagic, GIOPVersion.V1_2,
          FLAG_NO_FRAG_BIG_ENDIAN,
          Message.GIOPMessageError, 0);
    } else {
      throw wrapper.giopVersionError(
          CompletionStatus.COMPLETED_MAYBE);
    }
  }

  public static FragmentMessage createFragmentMessage(GIOPVersion gv) {
    // This method is not currently used.
    // New fragment messages are always created from existing messages.
    // Creating a FragmentMessage from InputStream is done in
    // createFromStream(..)
    return null;
  }

  public static int getRequestId(Message msg) {
    switch (msg.getType()) {
      case GIOPRequest:
        return ((RequestMessage) msg).getRequestId();
      case GIOPReply:
        return ((ReplyMessage) msg).getRequestId();
      case GIOPLocateRequest:
        return ((LocateRequestMessage) msg).getRequestId();
      case GIOPLocateReply:
        return ((LocateReplyMessage) msg).getRequestId();
      case GIOPCancelRequest:
        return ((CancelRequestMessage) msg).getRequestId();
      case GIOPFragment:
        return ((FragmentMessage) msg).getRequestId();
    }

    throw wrapper.illegalGiopMsgType(
        CompletionStatus.COMPLETED_MAYBE);
  }

  /**
   * Set a flag in the given buffer (fragment bit, byte order bit, etc)
   */
  public static void setFlag(ByteBuffer byteBuffer, int flag) {
    byte b = byteBuffer.get(6);
    b |= flag;
    byteBuffer.put(6, b);
  }

  /**
   * Clears a flag in the given buffer
   */
  public static void clearFlag(byte[] buf, int flag) {
    buf[6] &= (0xFF ^ flag);
  }

  private static void AreFragmentsAllowed(byte major, byte minor, byte flag,
      byte msgType) {

    if ((major == 0x01) && (minor == 0x00)) { // 1.0
      if (msgType == GIOPFragment) {
        throw wrapper.fragmentationDisallowed(
            CompletionStatus.COMPLETED_MAYBE);
      }
    }

    if ((flag & MORE_FRAGMENTS_BIT) == MORE_FRAGMENTS_BIT) {
      switch (msgType) {
        case GIOPCancelRequest:
        case GIOPCloseConnection:
        case GIOPMessageError:
          throw wrapper.fragmentationDisallowed(
              CompletionStatus.COMPLETED_MAYBE);
        case GIOPLocateRequest:
        case GIOPLocateReply:
          if ((major == 0x01) && (minor == 0x01)) { // 1.1
            throw wrapper.fragmentationDisallowed(
                CompletionStatus.COMPLETED_MAYBE);
          }
          break;
      }
    }
  }

  /**
   * Construct an ObjectKey from a byte[].
   *
   * @return ObjectKey the object key.
   */
  static ObjectKey extractObjectKey(byte[] objKey, ORB orb) {

    try {
      if (objKey != null) {
        ObjectKey objectKey =
            orb.getObjectKeyFactory().create(objKey);
        if (objectKey != null) {
          return objectKey;
        }
      }
    } catch (Exception e) {
      // XXX log this exception
    }

    // This exception is thrown if any exceptions are raised while
    // extracting the object key or if the object key is empty.
    throw wrapper.invalidObjectKey();
  }

  /**
   * Extract the object key from TargetAddress.
   *
   * @return ObjectKey the object key.
   */
  static ObjectKey extractObjectKey(TargetAddress target, ORB orb) {

    short orbTargetAddrPref = orb.getORBData().getGIOPTargetAddressPreference();
    short reqAddrDisp = target.discriminator();

    switch (orbTargetAddrPref) {
      case ORBConstants.ADDR_DISP_OBJKEY:
        if (reqAddrDisp != KeyAddr.value) {
          throw new AddressingDispositionException(KeyAddr.value);
        }
        break;
      case ORBConstants.ADDR_DISP_PROFILE:
        if (reqAddrDisp != ProfileAddr.value) {
          throw new AddressingDispositionException(ProfileAddr.value);
        }
        break;
      case ORBConstants.ADDR_DISP_IOR:
        if (reqAddrDisp != ReferenceAddr.value) {
          throw new AddressingDispositionException(ReferenceAddr.value);
        }
        break;
      case ORBConstants.ADDR_DISP_HANDLE_ALL:
        break;
      default:
        throw wrapper.orbTargetAddrPreferenceInExtractObjectkeyInvalid();
    }

    try {
      switch (reqAddrDisp) {
        case KeyAddr.value:
          byte[] objKey = target.object_key();
          if (objKey != null) { // AddressingDisposition::KeyAddr
            ObjectKey objectKey =
                orb.getObjectKeyFactory().create(objKey);
            if (objectKey != null) {
              return objectKey;
            }
          }
          break;
        case ProfileAddr.value:
          IIOPProfile iiopProfile = null;
          TaggedProfile profile = target.profile();
          if (profile != null) { // AddressingDisposition::ProfileAddr
            iiopProfile = IIOPFactories.makeIIOPProfile(orb, profile);
            ObjectKey objectKey = iiopProfile.getObjectKey();
            if (objectKey != null) {
              return objectKey;
            }
          }
          break;
        case ReferenceAddr.value:
          IORAddressingInfo iorInfo = target.ior();
          if (iorInfo != null) { // AddressingDisposition::IORAddr
            profile = iorInfo.ior.profiles[iorInfo.selected_profile_index];
            iiopProfile = IIOPFactories.makeIIOPProfile(orb, profile);
            ObjectKey objectKey = iiopProfile.getObjectKey();
            if (objectKey != null) {
              return objectKey;
            }
          }
          break;
        default: // this cannot happen
          // There is no need for a explicit exception, since the
          // TargetAddressHelper.read() would have raised a BAD_OPERATION
          // exception by now.
          break;
      }
    } catch (Exception e) {
    }

    // This exception is thrown if any exceptions are raised while
    // extracting the object key from the TargetAddress or if all the
    // the valid TargetAddress::AddressingDispositions are empty.
    throw wrapper.invalidObjectKey();
  }

  private static int readSize(byte b1, byte b2, byte b3, byte b4,
      boolean littleEndian) {

    int a1, a2, a3, a4;

    if (!littleEndian) {
      a1 = (b1 << 24) & 0xFF000000;
      a2 = (b2 << 16) & 0x00FF0000;
      a3 = (b3 << 8) & 0x0000FF00;
      a4 = (b4 << 0) & 0x000000FF;
    } else {
      a1 = (b4 << 24) & 0xFF000000;
      a2 = (b3 << 16) & 0x00FF0000;
      a3 = (b2 << 8) & 0x0000FF00;
      a4 = (b1 << 0) & 0x000000FF;
    }

    return (a1 | a2 | a3 | a4);
  }

  static void nullCheck(Object obj) {
    if (obj == null) {
      throw wrapper.nullNotAllowed();
    }
  }

  static SystemException getSystemException(
      String exClassName, int minorCode, CompletionStatus completionStatus,
      String message, ORBUtilSystemException wrapper) {
    SystemException sysEx = null;

    try {
      Class<?> clazz =
          SharedSecrets.getJavaCorbaAccess().loadClass(exClassName);
      if (message == null) {
        sysEx = (SystemException) clazz.newInstance();
      } else {
        Class[] types = {String.class};
        Constructor constructor = clazz.getConstructor(types);
        Object[] args = {message};
        sysEx = (SystemException) constructor.newInstance(args);
      }
    } catch (Exception someEx) {
      throw wrapper.badSystemExceptionInReply(
          CompletionStatus.COMPLETED_MAYBE, someEx);
    }

    sysEx.minor = minorCode;
    sysEx.completed = completionStatus;

    return sysEx;
  }

  public void callback(MessageHandler handler)
      throws java.io.IOException {
    handler.handleInput(this);
  }

  public ByteBuffer getByteBuffer() {
    return byteBuffer;
  }

  public void setByteBuffer(ByteBuffer byteBuffer) {
    this.byteBuffer = byteBuffer;
  }

  public int getThreadPoolToUse() {
    return threadPoolToUse;
  }

  public byte getEncodingVersion() {
    return this.encodingVersion;
  }

  public void setEncodingVersion(byte version) {
    this.encodingVersion = version;
  }

  private static void dprint(String msg) {
    ORBUtility.dprint("MessageBase", msg);
  }
}
